package dao;

import dto.TurnoDTO;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;

/**
 *
 * @author the_rub
 */
public class TurnoDAO {

  public static final String Tab =
    "create table turno (" +
    "id serial," + //pk
    "fecha timestamp," +
    "id_medico int," +
    "id_paciente int," +
    "estado text," +
    "sobreturno bool," +
    "id_obra_social_consulta int," + //fk, para las liquidaciones
    "id_plan_consulta int," + //fk, para las liquidaciones
    "n_afiliado_consulta int," +
    "monto_consulta decimal(12,2)," +
    "fecha_asignacion timestamp," +
    "id_user_asignacion int" +
    ");" +

    "alter table turno add constraint turno_obra_social_consulta_fk foreign key (id_obra_social_consulta) references obra_social(id);" +
    "alter table turno add constraint turno_plan_consulta_fk foreign key (id_plan_consulta) references obra_social_plan(id);"
  ;

  private static TurnoDAO dao = null;
  private final Connection c;

  public static TurnoDAO createDAO() throws SQLException  {
    if (dao == null) {
      dao = new TurnoDAO();
    }
    return dao;
  }

  private TurnoDAO() throws SQLException {
    c = DB.PQ().Conn();
  }

  public int LastID() throws SQLException {
    return DB.PQ().LastIDFrom("turno");
  }

  public LocalDateTime LastLocalDateTime() throws SQLException {
    Statement s = c.createStatement();
    ResultSet r = s.executeQuery("select max(fecha) from turno;");
    if (!r.next()) {
      throw new SQLException("?unknown bloody issue");
    }
    String date = r.getString(1);
    LocalDateTime d = date != null ? LocalDateTime.parse(r.getString(1),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null;
    r.close();
    s.close();
    return d;
  }

  public LocalDateTime LastLocalDateTimeFromMedico(int idMedico) throws SQLException {
    String query = String.format(
      "select max(fecha) from turno where id_medico = %d;", idMedico
    );
    Statement s = c.createStatement();
    ResultSet r = s.executeQuery(query);
    if (!r.next()) {
      throw new SQLException("?unknown bloody issue");
    }
    String date = r.getString(1);
    LocalDateTime d = date != null ? LocalDateTime.parse(r.getString(1), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null;
    r.close();
    s.close();
    return d;
  }

  public ArrayList<TurnoDTO> SearchByFechaMedico(LocalDate fecha, int idMedico) throws SQLException {
    return getTurnoDTOs(queryByFechaMedico(fecha, idMedico));
  }

  public ArrayList<TurnoDTO> SearchByFecha(LocalDateTime fecha) throws SQLException {
    return getTurnoDTOs(queryByFecha(fecha));
  }

  public ArrayList<TurnoDTO> SearchByMedicoID(int idMedico) throws SQLException {
    return getTurnoDTOs(queryByMedicoID(idMedico));
  }

  public ArrayList<TurnoDTO> SearchByPacienteID(int idPaciente, String estado) throws SQLException {
    return getTurnoDTOs(queryByPacienteID(idPaciente, estado));
  }

  public ArrayList<TurnoDTO> SearchByEstado(String s) throws SQLException {
    return getTurnoDTOs(queryByEstado(s));
  }

  public ArrayList<TurnoDTO> SearchTurnosAsistidosByObraSocialFromDateToDate(int idObraSocial, LocalDate fechaDesde, LocalDate fechaHasta, boolean orderByMedico) throws SQLException {
    return getTurnoDTOs(queryTurnosAsistidosByObraSocialFromDateToDate(idObraSocial, fechaDesde, fechaHasta, orderByMedico));
  }

  public ArrayList<TurnoDTO> SearchAsignadosHasta(LocalDate fecha) throws SQLException {
    return getTurnoDTOs(queryAsignadosHasta(fecha));
  }

  private String queryByFechaMedico(LocalDate fecha, int idMedico) throws SQLException {
    String query = String.format(
      "select * from turno where '%s' <= fecha and fecha <= '%s' and id_medico = %d order by fecha, sobreturno desc, estado desc;",
      fecha.atStartOfDay(), fecha.atTime(23,59), idMedico
    );
    return query;
  }

  private String queryByFecha(LocalDateTime fecha) throws SQLException {
    String query = String.format(
      "select * from turno where fecha = '%s' order by fecha, sobreturno desc, estado desc;", fecha.toString()
    );
    return query;
  }

  private String queryByMedicoID(int idMedico) throws SQLException {
    String query = String.format(
      "select * from turno where id_medico = %d order by fecha, sobreturno desc, estado desc;",
      idMedico
    );
    return query;
  }

  private String queryByPacienteID(int idPaciente, String estado) throws SQLException {
    String query = String.format(
      "select * from turno where id_paciente = %d and estado = '%s' order by fecha, sobreturno desc, estado desc;",
      idPaciente, estado
    );
    return query;
  }

  private String queryByEstado(String estado) throws SQLException {
    String query = String.format(
      "select * from turno where estado = '%s' order by fecha, sobreturno desc;", estado
    );
    return query;
  }

  private String queryTurnosAsistidosByObraSocialFromDateToDate(int idObraSocial, LocalDate fechaDesde, LocalDate fechaHasta, boolean orderByMedico) throws SQLException {
    String query = String.format(
      "select t.* from turno t, medico m where t.id_medico = m.id and t.estado = 'Asistido' and t.id_obra_social_consulta = %d and '%s' <= t.fecha and t.fecha <= '%s' order %s m.apellido, t.fecha;",
      idObraSocial, fechaDesde.atStartOfDay(), fechaHasta.atTime(23,59), orderByMedico ? "by" : "by date(t.fecha),"
    );
    return query;
  }

  private String queryAsignadosHasta(LocalDate fecha) throws SQLException {
    String query = String.format(
      "select * from turno where fecha <= '%s' and estado = '%s';",
      fecha.atTime(23,59), "Asignado"
    );
    return query;
  }

  private ArrayList<TurnoDTO> getTurnoDTOs(String query) throws SQLException {
    Statement s = c.createStatement();
    ResultSet r = s.executeQuery(query);
    ArrayList<TurnoDTO> turnos = new ArrayList<>();
    while (r.next()) {
      turnos.add(
        new TurnoDTO(
          r.getInt("id"),
          LocalDateTime.parse(r.getString("fecha"),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
          r.getInt("id_medico"),
          r.getInt("id_paciente"),
          r.getString("estado"),
          r.getBoolean("sobreturno"),
          r.getInt("id_obra_social_consulta"),
          r.getInt("id_plan_consulta"),
          r.getInt("n_afiliado_consulta"),
          r.getDouble("monto_consulta"),
          LocalDateTime.parse(r.getString("fecha_asignacion"),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
          r.getInt("id_user_asignacion")
        )
      );
    }
    r.close();
    s.close();
    return turnos;
  }

  public TurnoDTO TurnoDTO(int id) throws SQLException {
    Statement s = c.createStatement();
    ResultSet r = s.executeQuery(
      "select * from turno where id = " + id
    );
    TurnoDTO dto = null;
    if (r.next()) {
      dto = new TurnoDTO(
        r.getInt("id"),
        LocalDateTime.parse(r.getString("fecha"),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
        r.getInt("id_medico"),
        r.getInt("id_paciente"),
        r.getString("estado"),
        r.getBoolean("sobreturno"),
        r.getInt("id_obra_social_consulta"),
        r.getInt("id_plan_consulta"),
        r.getInt("n_afiliado_consulta"),
        r.getDouble("monto_consulta"),
        LocalDateTime.parse(r.getString("fecha_asignacion"),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
        r.getInt("id_user_asignacion")
      );
    }
    r.close();
    s.close();
    return dto; //si no existe devuelve null
  }

  // With great power comes great responsability.
  // Use with caution.
  public void Save(TurnoDTO t) throws SQLException{
    String query = String.format(
      "insert into turno values (default, '%s', %d, %d, '%s', %b, %d, %d, %d, %s, '%s', %d);",
      t.getFecha(), t.getIdMedico(), t.getIdPaciente(), t.getEstado(), t.isSobreturno(), t.getIdObraSocialConsulta(), t.getIdPlanConsulta(), t.getNAfiliadoConsulta(), t.getMontoConsulta(), t.getFechaAsignacion(), t.getIdUserAsignacion()
    );
    Statement s = c.createStatement();
    s.executeUpdate(query);
    s.close();
    t.setId(LastID());
  }

  // use with caution
  public void DeleteSobreturno(int id) throws SQLException {
    Statement s = c.createStatement();
    s.executeUpdate("delete from turno where sobreturno and id = " + id);
    s.close();
  }

  public TurnoDTO Edit(int id, int idPaciente, String estadoViejo, String estadoNuevo, Integer idObraSocialConsulta, Integer idPlanConsulta, int nAfiliadoConsulta, double montoConsulta, LocalDateTime fechaAsignacion, int idUserAsignacion) throws SQLException {
    String query = String.format(
      "update turno set id_paciente = %d, estado = '%s', id_obra_social_consulta = %d, id_plan_consulta = %d, n_afiliado_consulta = %d, monto_consulta = %s, fecha_asignacion = '%s', id_user_asignacion = %d where estado = '%s' and id = %d;",
      idPaciente, estadoNuevo, idObraSocialConsulta, idPlanConsulta, nAfiliadoConsulta, montoConsulta, fechaAsignacion, idUserAsignacion, estadoViejo, id
    );
    Statement s = c.createStatement();
    int nil = s.executeUpdate(query);
    s.close();

    if (nil == 0) {
      throw new IllegalArgumentException("?CINP");
    }
    return TurnoDTO(id);
  }

  public TurnoDTO EditEstado(int id, String estadoViejo, String estadoNuevo) throws SQLException {
    String query = String.format(
      "update turno set estado = '%s' where estado = '%s' and id = %d;",
      estadoNuevo, estadoViejo, id
    );
    Statement s = c.createStatement();
    int nil = s.executeUpdate(query);
    s.close();

    if (nil == 0) {
      throw new IllegalArgumentException("?CINP");
    }
    return TurnoDTO(id);
  }

}
